/**
 * Created by zhangguoyu on 16/4/30.
 */

var model = require('../../db/Model');
var util = require('util');
var Utils = require('../../utils/Utils');
var Identities = require('./../Identities');
var co = require('co');
var logModelSchema = require('./LogModelSchema');
var AppLogs = require('./AppLogs');
var LogUtils = require('../../utils/log/LogUtils');

var BaseInsertBuilder = function (appToken) {
  this.$appToken = appToken;
};

BaseInsertBuilder.prototype.$onBuildDoc = function (savedDoc, logDoc) {
  var dateAndtime = logDoc.datetime.split(' ');
  savedDoc.date = dateAndtime[0];
  savedDoc.time = dateAndtime[1];
  savedDoc.timestamp = logDoc.timestamp;
  savedDoc.type = logDoc.type;

  var content = logDoc.content;
  savedDoc.appPackage = content.package;
  savedDoc.appVersion = content.appversion;
  savedDoc.appToken = this.$appToken;
  savedDoc.platform = content.platform;
  savedDoc.flavor = content.flavor;
  savedDoc.userId = content.userid;
  savedDoc.isNewUser = content.isNewUser;
  savedDoc.stbModule = content.stbmodule;
  savedDoc.biz = content.biz;
  savedDoc.actionCode = content.actioncode;
};

BaseInsertBuilder.prototype.onBuildDoc = function (savedDoc, logDoc) {
  this.$onBuildDoc(savedDoc, logDoc)
};

BaseInsertBuilder.prototype.build = function (logDoc) {
  var doc = {
    data: {}
  };
  this.onBuildDoc(doc, logDoc);
  return doc;
};

var AppInsertBuilder = function (appToken) {
  BaseInsertBuilder.call(this, appToken)
};

AppInsertBuilder.prototype.onBuildDoc = function (savedDoc, logDoc) {
  this.$onBuildDoc(savedDoc, logDoc);
  savedDoc.data.app = logDoc.content.app;
};

var PageInsertBuilder = function (appToken, pageName) {
  BaseInsertBuilder.call(this, appToken);
  this.$pageName = pageName;
};

PageInsertBuilder.prototype.onBuildDoc = function (savedDoc, logDoc) {
  this.$onBuildDoc(savedDoc, logDoc);
  savedDoc.data.pageCode = logDoc.content.pagecode;
  savedDoc.data.pageName = this.$pageName;
};

var ContentInsertBuilder = function (appToken, categoryName) {
  BaseInsertBuilder.call(this, appToken);
  this.$categoryName = categoryName;
};

ContentInsertBuilder.prototype.onBuildDoc = function (savedDoc, logDoc) {
  this.$onBuildDoc(savedDoc, logDoc);
  savedDoc.data.categoryCode = logDoc.content.categorycode;
  savedDoc.data.categoryName = this.$categoryName;
  savedDoc.data.contentCode = logDoc.content.contentcode;
  savedDoc.data.contentName = logDoc.content.contentname;
  savedDoc.data.duration = parseInt(logDoc.content.duration) / 1000;
};

var EventInsertBuilder = function (appToken, eventName) {
  BaseInsertBuilder.call(this, appToken);
  this.$eventName = eventName;
};

EventInsertBuilder.prototype.onBuildDoc = function (savedDoc, logDoc) {
  this.$onBuildDoc(savedDoc, logDoc);
  savedDoc.data.eventCode = logDoc.content.eventcode;
  savedDoc.data.eventName = this.$eventName;
  savedDoc.data.eventdate = logDoc.content.eventdate;
};

util.inherits(AppInsertBuilder, BaseInsertBuilder);
util.inherits(PageInsertBuilder, BaseInsertBuilder);
util.inherits(ContentInsertBuilder, BaseInsertBuilder);
util.inherits(EventInsertBuilder, BaseInsertBuilder);

/**
 * 日志构造器工厂
 * @param appTokens
 * @param pages
 * @param categories
 * @param events
 * @constructor
 */
var BuilderFactory = function (appTokens, pages, categories, events) {
  var self = this;
  self._appTokens = appTokens;
  self._pages = pages;
  self._categories = categories;
  self._events = events;

  var _getFuncFactory = function (obj) {
    var UNKNOWN = 'unknown';
    return function (key) {
      if (key !== undefined && key !== null && obj[key]) {
        return obj[key];
      }
      return UNKNOWN;
    }
  };

  self._getAppToken = _getFuncFactory(self._appTokens);
  self._getPageName = _getFuncFactory(self._pages);
  self._getCategoryName = _getFuncFactory(self._categories);
  self._getEventName = _getFuncFactory(self._events);
};

BuilderFactory.prototype.createBuilder = function (type, pkg, code) {
  var builder = undefined;
  var appToken = this._getAppToken(pkg);
  switch (type) {
    case 'app':
      builder = new AppInsertBuilder(appToken);
      break;
    case 'page':
      builder = new PageInsertBuilder(appToken, this._getPageName(code));
      break;
    case 'content':
      builder = new ContentInsertBuilder(appToken, this._getCategoryName(code));
      break;
    case 'event':
      builder = new EventInsertBuilder(appToken, this._getEventName(code));
      break;
    default: break;
  }
  return builder;
};

var TempLogsManager = function () {
  
  var self = this;

  var _init = function () {
    self._appTokens = {};
    self._pages = {};
    self._categories = {};
    self._events = {};

    self._builderFactory = new BuilderFactory(self._appTokens, self._pages, self._categories, self._events);

    self._setArgs = function (item) {
      switch (item.category) {
        case 'app':
          self._appTokens[item.code] = item.appToken;
          break;
        case 'page':
          self._pages[item.code] = item.name;
          break;
        case 'category':
          self._categories[item.code] = item.name;
          break;
        case 'event':
          self._events[item.code] = item.name;
          break;
        default: break;
      }
    };

    self._convert = function (logDoc, type) {
      var logContent = logDoc.content;
      var key = type + 'code';
      if (type === 'content') {
        key = 'categorycode';
      }
      var builder = self._builderFactory.createBuilder(type, logContent.package, logDoc.content[key]);
      if (builder) {
        return builder.build(logDoc);
      } else {
        return undefined;
      }
    };
  };

  _init();

};

TempLogsManager.prototype.onStart = function () {
  var self = this;
  return new Promise(function (resolve, reject) {
    co(function * () {
      var querySet = yield Identities.$manager.find({ category: { $in: ['app', 'page', 'category', 'event'] } });
      if (querySet) {
        querySet.forEach(function (item) {
          self._setArgs(item);
        });
      }
      resolve(true);
    }).catch(function (err) {
      reject(err);
    });
  });
};

/**
 * 保存日志
 * @param logDoc 日志文档
 * @param func 回调
 * @returns {Promise}
 */
TempLogsManager.prototype.saveLog = function (logDoc, func) {
  return this.saveLogWithType(logDoc, logDoc.type, func)
};

TempLogsManager.prototype.saveLogs = function (logDocs, func) {
  var self = this;
  var savedDocs = [];
  if (logDocs && logDocs.length) {
    logDocs.forEach(function (item) {
      var d = self._convert(item, item.type);
      if (d) {
        savedDocs.push(d);
      }
    });
  }
  return self.batchInsert(savedDocs, func)
};

/**
 * 按日志类型保存日志
 * @param logDoc 日志文档
 * @param type 日志类型
 * @param func 回调
 * @returns {Promise}
 */
TempLogsManager.prototype.saveLogWithType = function (logDoc, type, func) {
  return this.insert(this._convert(logDoc, type), func);
};

/**
 * 将临时记录合入到AppLogs中,并清空自身的记录
 * @param func 回调
 * @returns {Promise}
 */
TempLogsManager.prototype.mergeToAppLogs = function (func) {
  var self = this;
  return new Promise(function(resolve, reject) {
    co(function * () {
      var result = {};
      var count = yield self.count();
      var split = 10000;
      var querySet = undefined;
      if (count > split) {
        var lastTimestamp = -1;
        var tail = count % split;
        var n = parseInt(count / split);
        var model = self.$model;
        if (n > 0) {
          for (var i = 0; i < n; i++) {
            LogUtils.info('merge temp from ' + i + ' progress ' + parseFloat((i / n) * 100) + ' %');
            querySet = yield model.find({ timestamp: { '$gt': lastTimestamp } }, { _id: 0 })
              .sort({ timestamp: 1 })
              .limit(split)
              .exec();
            if (querySet && querySet.length) {
              result = yield AppLogs.$manager.batchInsert(querySet);
              lastTimestamp = querySet[querySet.length - 1].timestamp;
              querySet = [];
            }
          }
        }

        if (tail) {
          querySet = yield model.find({ timestamp: { '$gt': lastTimestamp } }, { _id: 0 })
            .sort({ timestamp: 1 })
            .limit(tail)
            .exec();
        } else {
          querySet = undefined;
        }

      } else {
        querySet = yield self.all({ _id: 0 });
      }

      if (querySet && querySet.length) {
        result = yield AppLogs.$manager.batchInsert(querySet);
      }

      LogUtils.info('merge temp end');

      if (count > 0) {
        LogUtils.info('drop temp');
        yield self.drop();
      }

      Utils.callFunc(func, self, undefined, result);
      resolve(result || {});
    }).catch(function (err) {
      Utils.callFunc(func, self, err);
      LogUtils.error(err.toString());
      reject(err);
    });
  });
};

TempLogsManager.prototype.onEvent = function (model, event, result) {
  if (model === Identities.$name && event === 'insert') {
    this._setArgs(result);
  }
};

module.exports = model.create('TempLogs', logModelSchema, TempLogsManager);
